Skip to content

[C-655] add chainlink data streams oracle#342

Merged
aarmoa merged 4 commits intodevfrom
c-655/add_chainlink_data_streams_oracle
Jan 23, 2026
Merged

[C-655] add chainlink data streams oracle#342
aarmoa merged 4 commits intodevfrom
c-655/add_chainlink_data_streams_oracle

Conversation

@aarmoa
Copy link
Copy Markdown
Collaborator

@aarmoa aarmoa commented Dec 22, 2025

  • Adds support for the new Chainlink Data Streams oracle messages

Solves C-655

Summary by CodeRabbit

  • New Features

    • Chainlink Data Streams support: relay message, verifier settings, new oracle queries and params.
    • Exchange v2: expanded matching engine, VWAP tracking, batch execution, spot/derivative features, new fee-discount staking and market fee toggles.
    • Permissions: wasm + EVM hook support; new stake-grant messages and post-only mode messages.
  • Bug Fixes

    • Improved validation and error handling across oracle and exchange flows.
  • Chores

    • Protobuf/codec updates, OFAC list extended, build targets adjusted.

✏️ Tip: You can customize this high-level summary in your review settings.

…71. Includes support for the new Chainlink oracle messages.
@aarmoa aarmoa requested a review from Copilot December 22, 2025 17:17
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 22, 2025

📝 Walkthrough

Walkthrough

This PR adds Chainlink Data Streams support to the oracle (messages, protos, params, verification hooks), expands Exchange v2 (derivative matching, spot matching, VWAP, fee-discounts, many new message types and validators), adjusts permissions/permissions protos for wasm/evm hooks, updates EVM precompile bindings (Failure events), and refactors EVM log decoding and various small APIs.

Changes

Cohort / File(s) Summary
Build / Makefile
Makefile
Updated clone targets and copy step: different branches/tags for injected repos; copy-chain-types no longer copies bandchain dir.
EVM precompiles & ABIs
chain/evm/precompiles/bank/*.abigen.go, chain/evm/precompiles/*/i_*.abigen.go
Added Failure event support (event structs, iterators, Filter/Watch/Parse) and reformatted ABI JSON literals across several precompile bindings.
EVM log decoding
chain/evm/types/events.go, chain/evm/types/utils.go
Removed EventTypeTxLog/AttributeKeyTxLog; changed DecodeMsgLogs/DecodeTxLogs signatures to decode directly from tx responses; removed events-based fallback helpers.
Exchange (core + v2) types & codec
chain/exchange/types/*.go, chain/exchange/types/v2/*.go, proto/injective/exchange/**
Large v2 additions: derivative matching/VWAP, spot matching, fee-discount staking, subaccount aggregators, trade fee types, new MarketI interfaces and Funding field, many new validators/messages, codec registrations, proposal fields and proto additions (has_disabled_minimal_protocol_fee, DisableMinimalProtocolFeeUpdate), and query HTTP path updates.
Exchange expected keepers & authz
chain/exchange/types/expected_keepers.go, chain/exchange/types/v2/authz_exchange_generic.go
Keeper interface signature changes (GetPricePairState scalingOptions, GetCumulativePrice returns base/quote, GetProviderPriceState) and simplified authz Accept() spend-limit flow.
Exchange message validation & messages
chain/exchange/types/msgs.go, chain/exchange/types/v2/msgs.go, chain/exchange/types/msgs.go
Added order validators (ValidateSpot/Derivative/BinaryOptionsMarketOrder), added MsgCancelPostOnlyMode/MsgActivatePostOnlyMode, duplicate-grantee detection for MsgAuthorizeStakeGrants, and batch create/update validation changes.
Exchange wasm/trades summary
chain/exchange/types/wasm_trade_summary.go, chain/exchange/types/wasm_trades.go
Reworked synthetic trade summary representation and validation; removed old constructors and moved to new summary/validate/init/update flow; added SyntheticTrade.IsReduceOnly and updated validations.
Exchange keys & deposit helpers
chain/exchange/types/key.go, chain/exchange/types/v2/deposit.go
Added transient VWAP key prefix and accessor; Deposit.IsNegative() helper.
Oracle: Chainlink Data Streams integration
chain/oracle/types/*.go, proto/injective/oracle/v1beta1/*, injective_data/chain_messages_list.json
New MsgRelayChainlinkPrices (msg/proto/gRPC), ChainlinkReport and ChainlinkDataStreamsPriceState messages, genesis field, query and event RPCs, new Params fields (verifier proxy, accept-unverified flag, gas limit), new error ErrChainlinkVerificationFailed, address validation helper, and removal of Band-related message registrations.
Oracle proposals & codec cleanup
chain/oracle/types/proposal.go, chain/oracle/types/codec.go
Removed multiple Band-related proposal implementations and legacy Amino registrations for Band messages/proposals.
Permissions protos & examples
proto/injective/permissions/v1beta1/*, examples/chain/permissions/*
Renamed params/fields (wasm/contract hook rename), added evm_hook field, updated MsgUpdateNamespace proto and examples to use WasmHook/EvmHook.
Params & validation (exchange/oracle)
chain/exchange/types/v2/params.go, chain/oracle/types/params.go
Added EVM address validation utility and enforced validation for EnforcedRestrictionsContracts; added ValidateChainlinkVerifierProxyContract and Chainlink-related Defaults/validation.
Proto edits (misc)
proto/injective/exchange/**, proto/injective/oracle/**, proto/injective/permissions/**
Many proto changes: oneof amino name fully-qualified changes, HTTP path updates, new enums/fields for market/proposal changes, expiry TWAP base/quote cumulative fields, and permission field renames/additions.
Misc deps & data files
go.mod, injective_data/ofac.json
Downgraded cometbft replace to inj.3; appended three addresses to OFAC list.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120+ minutes

Poem

🐇 I nibble bytes and hop through streams,
Chainlink whispers feed my dreams,
VWAP hops, fees dance in a line,
Wasm and EVM hooks now both shine,
A rabbit's patch — small, tidy, fine 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.19% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '[C-655] add chainlink data streams oracle' clearly summarizes the main change: adding Chainlink Data Streams oracle support. It is concise, specific, and relates directly to the core objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

The pull request adds support for Chainlink Data Streams oracle messages to the Injective chain. This integration extends the existing oracle module to handle a new oracle type alongside existing providers like Pyth and Stork.

Key changes:

  • Introduces ChainlinkDataStreams oracle type with price state management
  • Adds RPC methods for relaying Chainlink prices and querying price states
  • Implements permissions module enhancements to support separate WASM and EVM contract hooks
  • Updates market parameters to support minimal protocol fee disabling
  • Adds validation for Chainlink verifier proxy contract addresses

Reviewed changes

Copilot reviewed 61 out of 63 changed files in this pull request and generated no comments.

Show a summary per file
File Description
proto/injective/oracle/v1beta1/* Defines Chainlink Data Streams messages, events, and query endpoints
proto/injective/permissions/v1beta1/* Separates contract hooks into wasm_hook and evm_hook fields
proto/injective/exchange/v2/* Adds has_disabled_minimal_protocol_fee field to market proposals
chain/oracle/types/params.go Adds Chainlink verifier proxy contract validation
chain/oracle/types/oracle.go Registers ChainlinkDataStreams oracle type
go.mod Downgrades cometbft dependency version
exchange//pb/.go Generated protobuf code updates
Comments suppressed due to low confidence (6)

proto/injective/oracle/v1beta1/tx.proto:1

  • The comment incorrectly states this message is for updating Pyth prices, but the message is named MsgRelayChainlinkPrices. Update the comment to reference Chainlink Data Streams instead of Pyth.
    proto/injective/oracle/v1beta1/query.proto:1
  • Missing space between 'Streams' and 'price' in the comment.
    go.mod:1
  • The cometbft dependency is being downgraded from v1.0.1-inj.4 to v1.0.1-inj.3. Verify this version downgrade is intentional and won't introduce compatibility issues or missing features needed by this PR.
    proto/injective/exchange/v2/proposal.proto:1
  • The enum name DisableMinimalProtocolFeeUpdate is confusing because the values False and True suggest a boolean state rather than an update operation. Consider renaming to MinimalProtocolFeeStatus or using more descriptive value names like KEEP_CURRENT, ENABLE, DISABLE.
    proto/injective/exchange/v2/market.proto:1
  • The amino.oneof_name values include the full package prefix. Verify this is the intended format for amino encoding, as the previous version used simpler names like 'uncapped' and 'capped'. This change could affect serialization compatibility.
    proto/injective/exchange/v2/market.proto:1
  • The deprecation comment provides good context. Ensure migration guides or release notes document this breaking change and guide users to update their code to use the new fields.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (12)
proto/injective/permissions/v1beta1/params.proto (1)

15-16: Consider naming consistency and gas limit granularity.

Two observations:

  1. Naming inconsistency: This field is named contract_hook_max_gas, while permissions.proto and tx.proto now use explicit wasm_hook/evm_hook naming. Consider whether this should be renamed for consistency (e.g., to a more generic name) or if separate limits are needed.

  2. Single gas limit: With distinct WASM and EVM hooks now supported, verify whether a single shared gas limit is intentional. EVM and CosmWasm have different gas semantics, so separate limits (wasm_hook_max_gas / evm_hook_max_gas) might provide finer control.

Makefile (1)

7-7: Verify that the feature branch reference is intentional.

The clone-injective-core target now references a feature branch (c-655/add_chainlink_data_streams_oracle) instead of a tagged release. While this is appropriate during development, consider:

  1. Before merging this PR, the core dependency should likely point to a stable release tag
  2. Feature branches can be rebased or force-pushed, leading to non-reproducible builds
  3. If this is intentional for coordinated development across repositories, document the dependency relationship
chain/exchange/types/v2/subaccount.go (1)

77-85: Consider adding nil guard for defensive coding.

AddSubaccountOrder assumes d and d.Order are non-nil. If called with a nil argument, this will panic. If callers are guaranteed to pass valid data, this is fine; otherwise, consider adding a nil check.

🔎 Optional defensive nil check
 func (r *SubaccountOrderResults) AddSubaccountOrder(d *SubaccountOrderData) {
+	if d == nil || d.Order == nil {
+		return
+	}
 	if d.Order.IsReduceOnly {
 		r.ReduceOnlyOrders = append(r.ReduceOnlyOrders, d)
chain/exchange/types/v2/spot.go (2)

22-35: Silent fallthrough for unknown OrderFillType may mask bugs.

The default return of TransientSellOrderbookFills for unknown fill types could hide programming errors. Consider returning nil to make unhandled cases explicit, or panic if this represents an invariant violation.

🔎 Suggested fix
 func (r *SpotOrderbookMatchingResults) GetOrderbookFills(fillType OrderFillType) *OrderbookFills {
 	switch fillType {
 	case RestingLimitBuy:
 		return r.RestingBuyOrderbookFills
 	case RestingLimitSell:
 		return r.RestingSellOrderbookFills
 	case TransientLimitBuy:
 		return r.TransientBuyOrderbookFills
 	case TransientLimitSell:
 		return r.TransientSellOrderbookFills
+	default:
+		return nil
 	}
-
-	return r.TransientSellOrderbookFills
 }

228-240: Minor: simplify variable declaration.

The existingVwapData declaration and immediate assignment can be combined.

🔎 Simplified variable initialization
 func (p *SpotVwapInfo) ApplyVwap(marketID common.Hash, newVwapData *SpotVwapData) {
-	var existingVwapData *SpotVwapData
-
-	existingVwapData = (*p)[marketID]
+	existingVwapData := (*p)[marketID]
 	if existingVwapData == nil {
 		existingVwapData = NewSpotVwapData()
 		(*p)[marketID] = existingVwapData
 	}
chain/exchange/types/v2/fee_discounts.go (1)

274-321: Incorrect slice capacity and inefficient sort comparison.

  1. Line 278 uses len(info.AccountFeeTiers) as capacity for subaccountVolumes, but iterates over the nested SubaccountMarketVolumeContributions map.

  2. Lines 301-305 use append inside the sort comparison function, which creates temporary allocations on every comparison call. Consider pre-computing the concatenated keys.

🔎 Proposed optimization
 func (info *FeeDiscountStakingInfo) GetSortedSubaccountAndMarketVolumes() (
 	[]*SubaccountVolumeContribution,
 	[]*MarketVolumeContribution,
 ) {
-	subaccountVolumes := make([]*SubaccountVolumeContribution, 0, len(info.AccountFeeTiers))
+	subaccountVolumes := make([]*SubaccountVolumeContribution, 0)
 	marketVolumeTracker := make(map[common.Hash]VolumeRecord)
 
 	// ... iteration code unchanged ...
 
 	info.AccountVolumesMux.RUnlock()
 
+	// Pre-compute sort keys to avoid allocations in comparison
+	type sortKey struct {
+		idx int
+		key []byte
+	}
+	keys := make([]sortKey, len(subaccountVolumes))
+	for i, sv := range subaccountVolumes {
+		keys[i] = sortKey{idx: i, key: append(sv.SubaccountID.Bytes(), sv.MarketID.Bytes()...)}
+	}
+	sort.SliceStable(keys, func(i, j int) bool {
+		return bytes.Compare(keys[i].key, keys[j].key) < 0
+	})
+	sorted := make([]*SubaccountVolumeContribution, len(subaccountVolumes))
+	for i, k := range keys {
+		sorted[i] = subaccountVolumes[k.idx]
+	}
+	subaccountVolumes = sorted
-	sort.SliceStable(subaccountVolumes, func(i, j int) bool {
-		return bytes.Compare(
-			append(subaccountVolumes[i].SubaccountID.Bytes(), subaccountVolumes[i].MarketID.Bytes()...),
-			append(subaccountVolumes[j].SubaccountID.Bytes(), subaccountVolumes[j].MarketID.Bytes()...),
-		) < 0
-	})
proto/injective/exchange/v2/proposal.proto (1)

82-86: Consider clarifying field naming for readability.

The field has_disabled_minimal_protocol_fee uses a double-negative construction ("disabled" + negative enum values). While functionally correct and likely matching the market type's field name, it can be confusing:

  • True means "fee is disabled" (no fee)
  • False means "fee is not disabled" (fee applies)

If this naming is intentional to match the market state field, consider adding a brief comment to clarify the semantics. Otherwise, a positive framing like minimal_protocol_fee_enabled with inverted enum values might be clearer.

Also applies to: 490-493, 590-593

proto/injective/oracle/v1beta1/tx.proto (1)

167-181: Fix stale comment above MsgRelayChainlinkPrices

The comment still references Pyth instead of Chainlink, which can confuse readers.

Proposed comment fix
-// MsgRelayPythPrices defines a SDK message for updating Pyth prices
+// MsgRelayChainlinkPrices defines a SDK message for relaying Chainlink prices
 message MsgRelayChainlinkPrices {
chain/oracle/types/msgs.go (1)

406-433: MsgRelayChainlinkPrices wiring mirrors existing Pyth relay semantics

Route, Type, GetSignBytes, and GetSigners are implemented consistently with the other oracle messages, and ValidateBasic correctly enforces a valid bech32 sender address.

If the handler relies on at least one reports entry, you may optionally enforce len(msg.Reports) > 0 here to fail fast on obviously empty relays; otherwise, current behaviour matches the Pyth relay pattern.

chain/exchange/types/v2/msgs.go (1)

965-973: Centralized validation for market orders; post‑only now rejected for market messages

The new helpers and their wiring introduce a clear, consistent validation policy:

  • ValidateSpotMarketOrder / ValidateDerivativeMarketOrder / ValidateBinaryOptionsMarketOrder:
    • Reject BUY_PO / SELL_PO for all market orders, which aligns with “post‑only” being a limit‑order concept.
    • For binary options, additionally reject conditional types for market orders and reuse the existing binary band validation via order.ValidateBasic(senderAddr, true).
  • MsgCreateSpotMarketOrder, MsgCreateDerivativeMarketOrder, and MsgCreateBinaryOptionsMarketOrder now delegate to these helpers.
  • MsgBatchUpdateOrders.ValidateBasic now applies the same helpers to SpotMarketOrdersToCreate, DerivativeMarketOrdersToCreate, and BinaryOptionsMarketOrdersToCreate, ensuring single and batched flows share identical rules.

Net effect: any client that previously sent post‑only market orders will now receive ErrInvalidOrderTypeForMessage. That looks intentional but is a behavioural change worth being aware of.

As a minor defensive improvement, you could also nil‑check the order pointer in each helper before dereferencing, in case a malformed batch ever contained a nil entry.

Also applies to: 2110-2126, 2727-2752

chain/exchange/types/v2/derivative.go (2)

906-908: Remove commented-out debug code.

This commented-out debug statement should be removed before merging.

🔎 Proposed fix
 		if position.Position != nil {
 			positions = append(positions, position.Position)
 			nonNilPositionSubaccountIDs = append(nonNilPositionSubaccountIDs, subaccountID)
 		}
-
-		// else {
-		// 	fmt.Println("❌ position is nil for subaccount", subaccountID.Hex())
-		// }
 	}

134-146: Consider documenting the nil position indicator pattern.

SetPosition silently returns when position is nil, while SetPositionIndicator explicitly sets a nil position. This asymmetry could lead to confusion. Consider adding a brief comment explaining the intended usage pattern for position indicators.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a9bfddd and 31af78e.

⛔ Files ignored due to path filters (20)
  • chain/exchange/types/exchange.pb.go is excluded by !**/*.pb.go
  • chain/exchange/types/query.pb.go is excluded by !**/*.pb.go
  • chain/exchange/types/v2/market.pb.go is excluded by !**/*.pb.go
  • chain/exchange/types/v2/proposal.pb.go is excluded by !**/*.pb.go
  • chain/exchange/types/v2/query.pb.go is excluded by !**/*.pb.go
  • chain/oracle/types/events.pb.go is excluded by !**/*.pb.go
  • chain/oracle/types/genesis.pb.go is excluded by !**/*.pb.go
  • chain/oracle/types/oracle.pb.go is excluded by !**/*.pb.go
  • chain/oracle/types/query.pb.go is excluded by !**/*.pb.go
  • chain/oracle/types/tx.pb.go is excluded by !**/*.pb.go
  • chain/permissions/types/params.pb.go is excluded by !**/*.pb.go
  • chain/permissions/types/permissions.pb.go is excluded by !**/*.pb.go
  • chain/permissions/types/tx.pb.go is excluded by !**/*.pb.go
  • exchange/auction_rpc/pb/goadesign_goagen_injective_auction_rpc.pb.go is excluded by !**/*.pb.go
  • exchange/auction_rpc/pb/goadesign_goagen_injective_auction_rpc_grpc.pb.go is excluded by !**/*.pb.go
  • exchange/derivative_exchange_rpc/pb/goadesign_goagen_injective_derivative_exchange_rpc.pb.go is excluded by !**/*.pb.go
  • exchange/event_provider_api/pb/goadesign_goagen_event_provider_api.pb.go is excluded by !**/*.pb.go
  • exchange/oracle_rpc/pb/goadesign_goagen_injective_oracle_rpc.pb.go is excluded by !**/*.pb.go
  • exchange/portfolio_rpc/pb/goadesign_goagen_injective_portfolio_rpc.pb.go is excluded by !**/*.pb.go
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (43)
  • Makefile
  • chain/evm/precompiles/bank/fixed_supply_bank_erc20.abigen.go
  • chain/evm/precompiles/bank/i_bank_module.abigen.go
  • chain/evm/precompiles/bank/mint_burn_bank_erc20.abigen.go
  • chain/evm/precompiles/exchange/i_exchange_module.abigen.go
  • chain/evm/precompiles/staking/i_staking_module.abigen.go
  • chain/evm/types/events.go
  • chain/evm/types/utils.go
  • chain/exchange/types/codec.go
  • chain/exchange/types/errors.go
  • chain/exchange/types/expected_keepers.go
  • chain/exchange/types/msgs.go
  • chain/exchange/types/proposal.go
  • chain/exchange/types/v2/authz_exchange_generic.go
  • chain/exchange/types/v2/codec.go
  • chain/exchange/types/v2/derivative.go
  • chain/exchange/types/v2/fee_discounts.go
  • chain/exchange/types/v2/market.go
  • chain/exchange/types/v2/msgs.go
  • chain/exchange/types/v2/proposal.go
  • chain/exchange/types/v2/spot.go
  • chain/exchange/types/v2/subaccount.go
  • chain/exchange/types/v2/trade.go
  • chain/oracle/types/errors.go
  • chain/oracle/types/msgs.go
  • chain/oracle/types/oracle.go
  • chain/oracle/types/params.go
  • go.mod
  • injective_data/chain_messages_list.json
  • injective_data/ofac.json
  • proto/injective/exchange/v1beta1/exchange.proto
  • proto/injective/exchange/v1beta1/query.proto
  • proto/injective/exchange/v2/market.proto
  • proto/injective/exchange/v2/proposal.proto
  • proto/injective/exchange/v2/query.proto
  • proto/injective/oracle/v1beta1/events.proto
  • proto/injective/oracle/v1beta1/genesis.proto
  • proto/injective/oracle/v1beta1/oracle.proto
  • proto/injective/oracle/v1beta1/query.proto
  • proto/injective/oracle/v1beta1/tx.proto
  • proto/injective/permissions/v1beta1/params.proto
  • proto/injective/permissions/v1beta1/permissions.proto
  • proto/injective/permissions/v1beta1/tx.proto
🧰 Additional context used
🧬 Code graph analysis (16)
chain/oracle/types/errors.go (1)
chain/oracle/types/key.go (1)
  • ModuleName (12-12)
chain/exchange/types/msgs.go (1)
chain/exchange/types/errors.go (2)
  • ErrInvalidAddress (91-91)
  • ErrInvalidStakeGrant (110-110)
chain/exchange/types/v2/trade.go (1)
chain/exchange/types/trading_rewards.go (1)
  • TradingRewardPoints (9-9)
chain/exchange/types/errors.go (6)
chain/exchange/types/key.go (1)
  • ModuleName (17-17)
chain/peggy/types/key.go (1)
  • ModuleName (10-10)
chain/permissions/types/codec.go (1)
  • ModuleName (12-12)
chain/oracle/types/key.go (1)
  • ModuleName (12-12)
chain/wasmx/types/key.go (1)
  • ModuleName (10-10)
chain/ocr/types/key.go (1)
  • ModuleName (12-12)
chain/exchange/types/codec.go (4)
chain/exchange/types/tx.pb.go (15)
  • MsgDecreasePositionMargin (2899-2911)
  • MsgDecreasePositionMargin (2915-2915)
  • MsgDecreasePositionMargin (2916-2918)
  • MsgLiquidatePosition (2573-2582)
  • MsgLiquidatePosition (2586-2586)
  • MsgLiquidatePosition (2587-2589)
  • MsgEmergencySettleMarket (2683-2690)
  • MsgEmergencySettleMarket (2694-2694)
  • MsgEmergencySettleMarket (2695-2697)
  • MsgAuthorizeStakeGrants (3504-3507)
  • MsgAuthorizeStakeGrants (3511-3511)
  • MsgAuthorizeStakeGrants (3512-3514)
  • MsgActivateStakeGrant (3593-3596)
  • MsgActivateStakeGrant (3600-3600)
  • MsgActivateStakeGrant (3601-3603)
chain/exchange/types/v2/tx.pb.go (15)
  • MsgDecreasePositionMargin (3050-3062)
  • MsgDecreasePositionMargin (3066-3066)
  • MsgDecreasePositionMargin (3067-3069)
  • MsgLiquidatePosition (2617-2626)
  • MsgLiquidatePosition (2630-2630)
  • MsgLiquidatePosition (2631-2633)
  • MsgEmergencySettleMarket (2834-2841)
  • MsgEmergencySettleMarket (2845-2845)
  • MsgEmergencySettleMarket (2846-2848)
  • MsgAuthorizeStakeGrants (3658-3663)
  • MsgAuthorizeStakeGrants (3667-3667)
  • MsgAuthorizeStakeGrants (3668-3670)
  • MsgActivateStakeGrant (3749-3754)
  • MsgActivateStakeGrant (3758-3758)
  • MsgActivateStakeGrant (3759-3761)
chain/exchange/types/v2/market.pb.go (4)
  • OpenNotionalCap_Uncapped (109-111)
  • OpenNotionalCap_Uncapped (116-116)
  • OpenNotionalCap_Capped (112-114)
  • OpenNotionalCap_Capped (117-117)
chain/exchange/types/v2/query.pb.go (4)
  • FullDerivativeMarket_PerpetualInfo (3321-3323)
  • FullDerivativeMarket_PerpetualInfo (3328-3328)
  • FullDerivativeMarket_FuturesInfo (3324-3326)
  • FullDerivativeMarket_FuturesInfo (3329-3329)
chain/exchange/types/expected_keepers.go (2)
chain/oracle/types/oracle.pb.go (5)
  • OracleType (32-32)
  • OracleType (89-91)
  • PriceRecords (1304-1308)
  • PriceRecords (1312-1312)
  • PriceRecords (1313-1315)
chain/oracle/types/query.pb.go (6)
  • ScalingOptions (1604-1607)
  • ScalingOptions (1611-1611)
  • ScalingOptions (1612-1614)
  • PricePairState (1726-1734)
  • PricePairState (1738-1738)
  • PricePairState (1739-1741)
chain/exchange/types/v2/msgs.go (1)
chain/exchange/types/errors.go (4)
  • ErrInvalidAddress (91-91)
  • ErrInvalidStakeGrant (110-110)
  • ErrInvalidOrderTypeForMessage (68-68)
  • ErrUnrecognizedOrderType (15-15)
chain/exchange/types/v2/subaccount.go (1)
chain/exchange/types/exchange.pb.go (3)
  • SubaccountOrderData (1812-1815)
  • SubaccountOrderData (1819-1819)
  • SubaccountOrderData (1820-1822)
chain/exchange/types/v2/codec.go (4)
chain/exchange/types/v2/tx.pb.go (12)
  • MsgDecreasePositionMargin (3050-3062)
  • MsgDecreasePositionMargin (3066-3066)
  • MsgDecreasePositionMargin (3067-3069)
  • MsgEmergencySettleMarket (2834-2841)
  • MsgEmergencySettleMarket (2845-2845)
  • MsgEmergencySettleMarket (2846-2848)
  • MsgAuthorizeStakeGrants (3658-3663)
  • MsgAuthorizeStakeGrants (3667-3667)
  • MsgAuthorizeStakeGrants (3668-3670)
  • MsgActivateStakeGrant (3749-3754)
  • MsgActivateStakeGrant (3758-3758)
  • MsgActivateStakeGrant (3759-3761)
chain/exchange/types/v2/proposal.pb.go (3)
  • DenomMinNotionalProposal (1260-1264)
  • DenomMinNotionalProposal (1268-1268)
  • DenomMinNotionalProposal (1269-1271)
chain/exchange/types/v2/market.pb.go (4)
  • OpenNotionalCap_Uncapped (109-111)
  • OpenNotionalCap_Uncapped (116-116)
  • OpenNotionalCap_Capped (112-114)
  • OpenNotionalCap_Capped (117-117)
chain/exchange/types/v2/query.pb.go (4)
  • FullDerivativeMarket_PerpetualInfo (3321-3323)
  • FullDerivativeMarket_PerpetualInfo (3328-3328)
  • FullDerivativeMarket_FuturesInfo (3324-3326)
  • FullDerivativeMarket_FuturesInfo (3329-3329)
chain/oracle/types/oracle.go (1)
chain/oracle/types/oracle.pb.go (1)
  • OracleType_ChainlinkDataStreams (48-48)
chain/exchange/types/proposal.go (5)
chain/exchange/types/v2/proposal.pb.go (3)
  • AdminInfo (614-617)
  • AdminInfo (621-621)
  • AdminInfo (622-624)
chain/exchange/types/proposal.pb.go (3)
  • AdminInfo (566-569)
  • AdminInfo (573-573)
  • AdminInfo (574-576)
chain/oracle/types/oracle.pb.go (2)
  • OracleType_Stork (47-47)
  • OracleType_ChainlinkDataStreams (48-48)
chain/exchange/types/errors.go (2)
  • ErrInvalidAddress (91-91)
  • ErrInvalidPermissions (112-112)
chain/exchange/types/market_admin.go (1)
  • MaxPerm (13-14)
chain/oracle/types/params.go (1)
chain/oracle/types/oracle.pb.go (3)
  • Params (93-98)
  • Params (102-102)
  • Params (103-105)
chain/evm/precompiles/bank/mint_burn_bank_erc20.abigen.go (1)
ethereum/util/contract.go (1)
  • BoundContract (27-35)
chain/exchange/types/v2/proposal.go (4)
chain/exchange/types/v2/proposal.pb.go (6)
  • DisableMinimalProtocolFeeUpdate (33-33)
  • DisableMinimalProtocolFeeUpdate (57-59)
  • DisableMinimalProtocolFeeUpdate_NoUpdate (36-36)
  • AdminInfo (614-617)
  • AdminInfo (621-621)
  • AdminInfo (622-624)
chain/oracle/types/oracle.pb.go (4)
  • OracleType_BandIBC (45-45)
  • OracleType_Provider (46-46)
  • OracleType_Stork (47-47)
  • OracleType_ChainlinkDataStreams (48-48)
chain/exchange/types/errors.go (2)
  • ErrInvalidAddress (91-91)
  • ErrInvalidPermissions (112-112)
chain/exchange/types/market_admin.go (1)
  • MaxPerm (13-14)
chain/exchange/types/v2/derivative.go (6)
chain/exchange/types/v2/market.go (2)
  • DerivativeMarket (211-213)
  • DerivativeMarket (223-225)
chain/exchange/types/deposit.go (3)
  • NewDepositDeltas (42-44)
  • DepositDeltas (40-40)
  • DepositDelta (11-14)
chain/exchange/types/trading_rewards.go (2)
  • NewTradingRewardPoints (11-15)
  • TradingRewardPoints (9-9)
chain/exchange/types/market.go (1)
  • NotionalToChainFormat (130-136)
chain/exchange/types/v2/common_utils.go (2)
  • DerivativeLimitOrderDelta (15-19)
  • DerivativeMarketOrderDelta (21-24)
exchange/portfolio_rpc/pb/goadesign_goagen_injective_portfolio_rpc.pb.go (3)
  • DerivativePosition (635-672)
  • DerivativePosition (687-687)
  • DerivativePosition (702-704)
chain/evm/precompiles/bank/fixed_supply_bank_erc20.abigen.go (1)
ethereum/util/contract.go (1)
  • BoundContract (27-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: run-tests
  • GitHub Check: lint
🔇 Additional comments (69)
proto/injective/permissions/v1beta1/permissions.proto (1)

13-14: LGTM – EVM hook support added correctly.

The field renaming from contract_hook to wasm_hook (field 2) and addition of evm_hook (field 8) are well-structured. Field numbers are preserved, ensuring protobuf wire compatibility.

Note: Renaming contract_hookwasm_hook changes the JSON field name, which could be a breaking change for clients using JSON encoding. Ensure downstream consumers are updated accordingly.

Also applies to: 26-28

proto/injective/permissions/v1beta1/tx.proto (1)

65-77: LGTM – Consistent with permissions.proto changes.

The SetContractHook type is appropriately reused for both wasm_hook (field 3) and evm_hook (field 8). Field numbers are preserved, maintaining wire compatibility.

Same consideration as noted in permissions.proto: the JSON field name change from contract_hook to wasm_hook may affect JSON-based clients.

go.mod (1)

256-256: No action required. The cometbft version change is part of the coordinated C-655 PR that updates proto definitions to chain v1.18.0 and indexer v1.17.71 and adds Chainlink oracle support, as evidenced by the oracle.proto file changes showing new ChainlinkDataStreams enum and Chainlink-related configuration parameters.

chain/oracle/types/errors.go (1)

51-51: LGTM!

The new error definition follows the existing error registration pattern and provides a clear message for Chainlink report verification failures. The error code (44) is sequential and appropriate.

chain/evm/precompiles/staking/i_staking_module.abigen.go (1)

40-40: LGTM - Generated code with improved ABI format.

The ABI string has been reformatted to use explicit field names (type, name, internalType), which improves readability while maintaining functional equivalence. Since this is generated code (as indicated by the file header), the change results from regenerating the bindings with an updated toolchain.

chain/evm/precompiles/bank/i_bank_module.abigen.go (1)

34-34: LGTM - Generated code with improved ABI format.

Consistent with the staking module binding, the ABI string has been reformatted to use explicit field names. This is generated code with improved readability and no functional changes.

chain/exchange/types/errors.go (1)

120-121: LGTM!

The two new error definitions follow the established error registration pattern and use sequential error codes (112, 113). The error messages clearly describe the failure conditions related to open notional cap validation and offsetting position requirements.

chain/evm/types/events.go (1)

15-15: No internal references to removed constants found.

The constants EventTypeTxLog and AttributeKeyTxLog are not referenced elsewhere in the codebase, so the removal does not break internal dependencies. If these constants are part of a public API, ensure external consumers are notified of the deprecation or provide migration documentation separately.

proto/injective/exchange/v1beta1/exchange.proto (1)

23-28: Update amino.oneof_name changes to use fully qualified names for JSON encoding consistency.

The amino.oneof_name attributes have been updated to fully qualified names (injective.exchange.v1beta1.OpenNotionalCapUncapped and injective.exchange.v1beta1.OpenNotionalCapCapped). This improves naming consistency in amino JSON output but does not affect protobuf wire format compatibility (field numbers remain unchanged). No migration strategy is required unless downstream systems explicitly depend on parsing the old short-form strings from amino JSON output.

proto/injective/exchange/v2/query.proto (1)

265-265: The HTTP endpoint paths in the v2 proto file show version prefixes consistent with the v2 API structure. However, PositionsInMarket is a new endpoint introduced in V2 (not present in v1beta1), and SubaccountPositions maintained its endpoint structure with only the version prefix updated from v1beta1 to v2. These are not breaking changes to existing REST API endpoints but rather additions and version-specific routing, which is the expected behavior for versioned APIs.

Likely an incorrect or invalid review comment.

chain/exchange/types/proposal.go (3)

1655-1665: LGTM! Clean validation implementation for AdminInfo.

The ValidateBasic method correctly:

  • Validates the Admin address using sdk.AccAddressFromBech32 only when non-empty
  • Validates AdminPermissions against MaxPerm to ensure no invalid permission bits are set
  • Uses appropriate error types from the errors registry (ErrInvalidAddress, ErrInvalidPermissions)

679-686: LGTM! New oracle type correctly added to validation.

The OracleType_ChainlinkDataStreams is properly included in the valid oracle types switch case, consistent with its definition in the oracle protobuf (OracleType_ChainlinkDataStreams = 13).


290-294: LGTM! Consistent AdminInfo validation integration across proposals.

The AdminInfo.ValidateBasic() call is consistently integrated into all relevant proposal types with proper nil-check and early error return. This centralizes admin validation logic and maintains consistency.

Also applies to: 428-432, 535-539, 802-807, 903-908

chain/oracle/types/params.go (3)

55-59: Verify the default AcceptUnverifiedChainlinkDataStreamsReports = true.

Setting this to true by default means unverified Chainlink Data Streams reports will be accepted when no verifier proxy contract is configured. This may be intentional for initial rollout or testing, but please confirm this is the desired security posture for production.


116-132: LGTM! Proper Ethereum address validation.

The ValidateChainlinkVerifierProxyContract function correctly:

  • Accepts interface{} for compatibility with param validation patterns
  • Allows empty string for optional configuration
  • Uses common.IsHexAddress from go-ethereum for robust hex address validation

72-81: LGTM! Validation properly extended for new params.

The Validate() method correctly validates both contract addresses with descriptive error wrapping to identify which field failed.

chain/evm/types/utils.go (2)

87-97: LGTM! Simplified API with proper bounds checking.

The DecodeMsgLogs function now has a cleaner signature and includes proper bounds validation for msgIndex (lines 93-95) to prevent out-of-range access. The function correctly returns an error for invalid indices rather than panicking.


99-110: LGTM! Clean aggregation of logs across all responses.

The DecodeTxLogs function correctly iterates through all transaction responses and aggregates logs using the logsFromTxResponse helper, maintaining consistency with the single-message variant.

proto/injective/oracle/v1beta1/oracle.proto (3)

157-163: Verify intentional type difference for feed_id between messages.

ChainlinkDataStreamsPriceState.feed_id is defined as string (line 158), while ChainlinkReport.feed_id is defined as bytes (line 314).

This may be intentional (e.g., ChainlinkReport holds raw data from the report while ChainlinkDataStreamsPriceState stores a hex-encoded or readable version), but please confirm this is the intended design.

Also applies to: 313-318


11-19: LGTM! Params extended with proper field numbering.

The new Chainlink-related fields are added with sequential field numbers (2, 3, 4), maintaining protobuf wire compatibility. The field types are appropriate:

  • string for the contract address
  • bool for the feature flag
  • uint64 for the gas limit

21-36: LGTM! New oracle type properly added to enum.

ChainlinkDataStreams = 13 is correctly added following the existing sequence and maintains backward compatibility with existing enum values.

chain/exchange/types/expected_keepers.go (1)

31-59: Breaking interface changes require all external implementations to be updated.

The OracleKeeper interface in this SDK has breaking changes that affect any code implementing these methods:

  • GetPricePairState now requires scalingOptions *oracletypes.ScalingOptions parameter
  • GetCumulativePrice returns two values: (baseCumulative, quoteCumulative *sdkmath.LegacyDec)
  • GetHistoricalPriceRecords returns (entry *oracletypes.PriceRecords, omitted bool)
  • GetMixedHistoricalPriceRecords returns (mixed *oracletypes.PriceRecords, ok bool)

Any consumer of this SDK that implements the OracleKeeper interface must update their implementations to match these signatures.

chain/oracle/types/oracle.go (1)

40-41: LGTM!

The new chainlinkdatastreams case follows the established pattern of other oracle types in the switch statement.

proto/injective/oracle/v1beta1/genesis.proto (1)

45-47: LGTM!

The new chainlink_data_streams_price_states field follows the established pattern and uses the next sequential field number.

chain/exchange/types/v2/subaccount.go (1)

47-67: LGTM!

The constructor functions properly initialize all LegacyDec fields to zero and pre-allocate slices, following good initialization patterns.

proto/injective/exchange/v2/market.proto (3)

335-344: Well-documented deprecation.

The deprecation includes clear rationale and points to the new replacement fields. Retaining the field number ensures backward compatibility with existing data.


351-364: LGTM!

The new expiration_twap_start_base_cumulative_price and expiration_twap_start_quote_cumulative_price fields properly address the limitation of the deprecated field by tracking base and quote cumulative prices separately.


109-111: Consistent addition of has_disabled_minimal_protocol_fee across market types.

The field is added uniformly to SpotMarket, BinaryOptionsMarket, and DerivativeMarket with consistent naming and documentation.

Also applies to: 187-190, 278-281

chain/exchange/types/v2/spot.go (3)

204-219: LGTM on VWAP calculation logic.

The ApplyExecution method correctly handles nil receivers by creating a new instance, and guards against nil/zero quantity inputs. The weighted average calculation (oldPrice * oldQty + newPrice * newQty) / totalQty is mathematically correct for VWAP.


93-124: LGTM on deposit delta handling.

The UpdateFromDepositDeltas method correctly handles:

  • Positive change amounts being added to available balance
  • Zero subaccount fallback to auction subaccount for fee recipients
  • Proper application of trader and fee recipient deltas

126-190: LGTM on batch execution event construction.

The function properly:

  • Preallocates slices with appropriate capacity
  • Skips zero-fill trades to avoid empty entries
  • Handles self-relayed trade fee calculation correctly
  • Returns nil event when no trades occurred
chain/exchange/types/v2/fee_discounts.go (5)

84-95: LGTM - ValidatorCache type and NewFeeDiscountConfig constructor.

The defensive nil check for stakingInfo and setting isQualified = false is appropriate.


97-121: LGTM - FeeDiscountConfig struct and GetFeeDiscountRate method.

Proper nil receiver check and correct RLock/RUnlock pattern for concurrent access.


123-162: LGTM - IncrementAccountVolumeContribution implementation.

Defensive check for negative amounts, proper mutex locking, and correct map update patterns.


164-217: LGTM - FeeDiscountStakingInfo struct and constructor.

All maps and mutexes are properly initialized.


323-383: LGTM - Remaining getter/setter methods.

Proper mutex usage throughout. SetAccountTierInfo, SetNewAccountTierTTL, AddCheckpoint, and AddInvalidGrant all correctly use Lock/Unlock.

injective_data/chain_messages_list.json (1)

128-128: LGTM - New Chainlink message type registration.

The new MsgRelayChainlinkPrices message type is correctly added and properly positioned alphabetically within the oracle module namespace. This aligns with the PR objective of adding Chainlink data streams oracle support.

proto/injective/oracle/v1beta1/events.proto (1)

90-93: LGTM - New Chainlink data streams event.

The EventSetChainlinkDataStreamsPrices message follows the established pattern used by similar events (EventSetStorkPrices, EventSetPythPrices) and correctly references the ChainlinkDataStreamsPriceState type from the imported oracle.proto.

chain/exchange/types/msgs.go (1)

2099-2111: LGTM - Duplicate grantee validation.

The implementation correctly detects and rejects duplicate grantees within a single MsgAuthorizeStakeGrants message. The error message includes the duplicate grantee address for debugging, and the validation is performed before the amount validation.

chain/exchange/types/v2/trade.go (1)

1-23: LGTM - TradeFeeData struct and constructor.

Clean implementation with proper initialization using math.LegacyZeroDec(). The struct provides a clear model for trade fee-related data, and the constructor correctly accepts the discounted rate while initializing other fields to zero.

chain/evm/precompiles/exchange/i_exchange_module.abigen.go (1)

1-2: Generated file - verify ABI source consistency.

This is an auto-generated binding file. The ABI update on line 206 (changing uint256 to ExchangeTypes.UFixed256x18 for price, quantity, margin, and related fields) appears consistent with the PR's goal of updating proto definitions for chain v1.18.0.

Ensure the Solidity interface source (IExchangeModule.sol or equivalent) was updated first, and this file was regenerated using abigen rather than manually edited.

Also applies to: 206-206

chain/exchange/types/v2/authz_exchange_generic.go (2)

115-118: Verify intended behavior when hold is missing from context.

When getHold returns ok = false (context key not set or value is nil), the method returns Accept: false. This means authorization will be denied if the caller forgets to set the hold in context.

Is this the intended behavior? Consider whether:

  1. Missing hold should be treated as zero spend (accept with unchanged limit), or
  2. Missing hold should explicitly reject (current behavior)

If the current strict behavior is intentional, consider adding a comment explaining why missing hold results in rejection.


110-131: LGTM - Clean spend limit enforcement logic.

The refactored Accept method implements a clear flow:

  1. No-limit case returns early (line 111-113)
  2. Missing hold context rejects authorization (line 115-118)
  3. Single-pass validation and deduction of spend limits (line 120-130)

The logic correctly handles the case where a hold denom isn't in the spend limit (returns false since AmountOf returns zero for missing denoms).

proto/injective/oracle/v1beta1/query.proto (1)

226-234: LGTM - New Chainlink query messages.

The new request/response message definitions follow the established patterns in this file (empty request for retrieving all states, response with repeated price state field).

chain/exchange/types/codec.go (3)

18-20: LGTM - Oneof interface registration for Amino.

Registering oneof interfaces (isOpenNotionalCap_Cap and isFullDerivativeMarket_Info) is necessary for proper Amino JSON serialization of protobuf oneof fields. This follows the correct pattern for handling oneofs in the Cosmos SDK legacy Amino codec.


41-45: LGTM - New message type registrations.

The new messages (MsgDecreasePositionMargin, MsgEmergencySettleMarket, MsgAuthorizeStakeGrants, MsgActivateStakeGrant) are correctly registered in both:

  • Legacy Amino codec (lines 41-45) for JSON serialization
  • Interface registry (lines 122-126) for protobuf/gRPC

Registration names follow the established exchange/MsgX convention.

Also applies to: 122-126


92-98: LGTM - Oneof implementation registrations.

Concrete oneof types are registered with clear, descriptive names that distinguish between the variants:

  • exchange/OpenNotionalCapUncapped and exchange/OpenNotionalCapCapped
  • exchange/FullDerivativeMarketPerpetualInfo and exchange/FullDerivativeMarketFuturesInfo

This enables proper Amino serialization/deserialization of these polymorphic types.

proto/injective/exchange/v2/proposal.proto (1)

17-21: LGTM - Tri-state enum for optional updates.

The DisableMinimalProtocolFeeUpdate enum correctly implements a tri-state pattern:

  • NoUpdate (0): Don't modify the existing value
  • False (1): Set the flag to false (enable minimal protocol fee)
  • True (2): Set the flag to true (disable minimal protocol fee)

This is the appropriate pattern for optional fields in update proposals where you need to distinguish between "don't change" and "set to false".

chain/evm/precompiles/bank/fixed_supply_bank_erc20.abigen.go (2)

34-35: Generated ABI/Bin update – no manual review needed

These large ABI/Bin string literals are generated and appear consistent with the added Failure event bindings; no manual changes suggested.


608-741: Failure event bindings mirror existing iterator/watch patterns correctly

The FixedSupplyBankERC20FailureIterator, FixedSupplyBankERC20Failure struct, and the FilterFailure/WatchFailure/ParseFailure methods follow the same patterns as the existing Approval/Transfer bindings and correctly use FilterLogs/WatchLogs without indexed topics. Looks good.

proto/injective/oracle/v1beta1/tx.proto (1)

46-50: Chainlink RPC wiring matches existing oracle Msg patterns

RelayChainlinkPrices is added consistently with other RPCs (Pyth, Stork, etc.) and uses the expected request/response message types. No issues here.

chain/oracle/types/msgs.go (1)

27-27: Chainlink relay type and sdk.Msg registration are consistent

TypeMsgRelayChainlinkPrices and the _ sdk.Msg = &MsgRelayChainlinkPrices{} registration align with the existing oracle message conventions.

Also applies to: 39-39

chain/evm/precompiles/bank/mint_burn_bank_erc20.abigen.go (2)

34-35: Generated ABI/Bin update – treated as codegen

The updated ABI/Bin literals reflect the new Failure event; assuming these came from regenerating bindings, there’s nothing to adjust manually.


744-877: MintBurn Failure event bindings are correct and consistent

The MintBurnBankERC20FailureIterator, MintBurnBankERC20Failure struct, and associated FilterFailure/WatchFailure/ParseFailure methods follow the established iterator and subscription patterns and correctly omit indexed topics for this event. All good.

chain/exchange/types/v2/msgs.go (2)

346-360: Stork timestamp comment accurately documents existing behaviour

The new comment about converting timestamps to nanoseconds matches the surrounding code and improves readability without changing semantics.


2227-2243: Duplicate grantee detection in MsgAuthorizeStakeGrants

The new seenGrantees map cleanly prevents duplicate Grantee entries within a single MsgAuthorizeStakeGrants, using validated bech32 strings as keys and returning ErrInvalidStakeGrant on duplicates. This is a sensible tightening of validation with minimal overhead.

chain/exchange/types/v2/codec.go (2)

38-40: LGTM! Oneof interface and concrete type registrations are properly structured.

The registration of oneof interfaces (isOpenNotionalCap_Cap, isFullDerivativeMarket_Info) and their concrete implementations (OpenNotionalCap_Uncapped, OpenNotionalCap_Capped, FullDerivativeMarket_PerpetualInfo, FullDerivativeMarket_FuturesInfo) follows the correct pattern for Amino codec compatibility with protobuf oneof fields.

Also applies to: 120-127


61-77: LGTM! New message types are properly registered.

The new messages (MsgDecreasePositionMargin, MsgEmergencySettleMarket, MsgAuthorizeStakeGrants, MsgActivateStakeGrant) are correctly registered in both RegisterLegacyAminoCodec and RegisterInterfaces, ensuring proper serialization and interface compliance.

Also applies to: 151-167

chain/exchange/types/v2/market.go (3)

107-110: LGTM! Maturation start condition correctly updated.

The IsStartingMaturation method now properly checks ExpirationTwapStartBaseCumulativePrice to determine if maturation is starting, which aligns with the TWAP-based calculation approach for expiry futures markets.


371-394: LGTM! Market solvency and balance delta utilities are well-implemented.

IsMarketSolvent provides a clear check for market fund adequacy. GetMarketBalanceDelta correctly handles the reduce-only case by adding the trade fee back before computing the delta, as reduce-only orders have fees deducted from payout rather than held separately.


12-16: LGTM! New MarketIDQuoteDenomMakerFee struct is well-defined.

The struct provides a clean grouping of market identification and maker fee information for fee-related operations.

chain/exchange/types/v2/proposal.go (4)

771-772: LGTM! Chainlink Data Streams oracle type support added.

The addition of oracletypes.OracleType_ChainlinkDataStreams to the valid oracle types in OracleParams.ValidateBasic() enables support for the new Chainlink Data Streams oracle, which is the core objective of this PR.


1894-1903: LGTM! AdminInfo.ValidateBasic() implementation is correct.

The validation properly:

  1. Validates the admin address using sdk.AccAddressFromBech32 only when non-empty
  2. Checks AdminPermissions against types.MaxPerm to ensure valid permission flags
  3. Uses appropriate error types from the exchange module

452-456: LGTM! Consistent AdminInfo.ValidateBasic() integration across proposals.

The AdminInfo validation is now consistently applied across SpotMarketLaunchProposal, PerpetualMarketLaunchProposal, and ExpiryFuturesMarketLaunchProposal, ensuring proper admin address and permission validation when provided.

Also applies to: 918-922, 1031-1035


1686-1706: LGTM! Constructor properly initializes all fields.

The NewBinaryOptionsMarketParamUpdateProposal constructor correctly accepts and assigns the hasDisabledMinimalProtocolFee parameter along with all other fields.

chain/exchange/types/v2/derivative.go (3)

14-41: LGTM! Order comparison logic is correct.

GetIsOrderLess correctly computes the distance from reference price for both buy and sell orders, and the sorting direction is properly controlled by isSortingFromWorstToBest.


749-765: LGTM! VWAP calculation handles edge cases properly.

ApplyExecution correctly guards against nil values and zero quantity, preventing division by zero when computing the weighted average price.


978-1064: LGTM! Batch event construction with proper delta application.

ApplyDeltasAndGetDerivativeOrderBatchEvent correctly applies deposit deltas, trading reward points, and constructs trade logs with proper fee calculations including self-relayed trade handling.

injective_data/ofac.json (1)

36-36: Verify OFAC addresses against official SDN list.

Three new Ethereum addresses have been added (lines 36, 62, 72). The addresses are validly formatted and the JSON structure is correct. However, confirm these addresses are from the official OFAC SDN (Specially Designated Nationals) list, as sanctions lists are compliance-critical.

Also applies to: 62-62, 72-72

"exchange/v2/AtomicMarketOrderFeeMultiplierScheduleProposal",
nil,
)
cdc.RegisterConcrete(&DenomMinNotionalProposal{}, "exchange/v2/DenomMinNotionalProposal", nil)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "codec.go" -path "*/exchange/types/v2/*" 2>/dev/null | head -20

Repository: InjectiveLabs/sdk-go

Length of output: 98


🏁 Script executed:

cat -n ./chain/exchange/types/v2/codec.go | sed -n '1,220p'

Repository: InjectiveLabs/sdk-go

Length of output: 13410


Register DenomMinNotionalProposal in govtypes.Content interface registry.

DenomMinNotionalProposal is registered in the Amino codec (line 104) but missing from the govtypes.Content interface implementations in RegisterInterfaces (lines 183-202). All other proposal types are consistently registered in both registries. Add it to maintain pattern consistency.

🤖 Prompt for AI Agents
In chain/exchange/types/v2/codec.go around line 104 and in the
RegisterInterfaces block at lines ~183-202, the DenomMinNotionalProposal type is
registered in the Amino codec but not added to the govtypes.Content interface
registrations; add DenomMinNotionalProposal to the RegisterInterfaces call so it
implements govtypes.Content alongside the other proposal types (place it with
the other proposal registrations in the same slice/map used for
govtypes.Content).

Comment on lines +77 to +80
notional := order.Fillable.Mul(order.Price)
fee := notional.Mul(positiveMakerFeePart)
remainingMargin := order.Margin.Mul(order.Fillable).Quo(order.Quantity)
cumulativeQuoteAmount = cumulativeQuoteAmount.Add(remainingMargin).Add(fee)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Potential division by zero when order.Quantity is zero.

If order.Quantity is zero, the calculation order.Margin.Mul(order.Fillable).Quo(order.Quantity) will panic. Consider adding a guard or ensuring orders with zero quantity are filtered upstream.

🔎 Proposed fix
+		if order.Quantity.IsZero() {
+			continue
+		}
+
 		notional := order.Fillable.Mul(order.Price)
 		fee := notional.Mul(positiveMakerFeePart)
 		remainingMargin := order.Margin.Mul(order.Fillable).Quo(order.Quantity)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
notional := order.Fillable.Mul(order.Price)
fee := notional.Mul(positiveMakerFeePart)
remainingMargin := order.Margin.Mul(order.Fillable).Quo(order.Quantity)
cumulativeQuoteAmount = cumulativeQuoteAmount.Add(remainingMargin).Add(fee)
if order.Quantity.IsZero() {
continue
}
notional := order.Fillable.Mul(order.Price)
fee := notional.Mul(positiveMakerFeePart)
remainingMargin := order.Margin.Mul(order.Fillable).Quo(order.Quantity)
cumulativeQuoteAmount = cumulativeQuoteAmount.Add(remainingMargin).Add(fee)
🤖 Prompt for AI Agents
In chain/exchange/types/v2/derivative.go around lines 77-80, the expression
order.Margin.Mul(order.Fillable).Quo(order.Quantity) can panic if order.Quantity
is zero; add a guard before performing the division (e.g., if
order.Quantity.IsZero() then handle by skipping this order, setting
remainingMargin to zero, or returning an error), or validate/filter out
zero-quantity orders upstream and document the assumption; ensure the chosen
behavior is explicit and covered by a unit test.

Comment on lines +859 to +863
// ComputeSyntheticVwapUnitDelta returns (price - markPrice) / markPrice
func (p *DerivativeVwapInfo) ComputeSyntheticVwapUnitDelta(marketID common.Hash) math.LegacyDec {
vwapInfo := p.PerpetualVwapInfo[marketID]
return vwapInfo.VwapData.Price.Sub(*vwapInfo.MarkPrice).Quo(*vwapInfo.MarkPrice)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Potential division by zero in ComputeSyntheticVwapUnitDelta.

If MarkPrice is nil or zero, the division vwapInfo.VwapData.Price.Sub(*vwapInfo.MarkPrice).Quo(*vwapInfo.MarkPrice) will panic. Additionally, if the marketID is not found in PerpetualVwapInfo, dereferencing vwapInfo will cause a nil pointer dereference.

🔎 Proposed fix with nil and zero checks
 func (p *DerivativeVwapInfo) ComputeSyntheticVwapUnitDelta(marketID common.Hash) math.LegacyDec {
 	vwapInfo := p.PerpetualVwapInfo[marketID]
+	if vwapInfo == nil || vwapInfo.MarkPrice == nil || vwapInfo.MarkPrice.IsZero() {
+		return math.LegacyZeroDec()
+	}
 	return vwapInfo.VwapData.Price.Sub(*vwapInfo.MarkPrice).Quo(*vwapInfo.MarkPrice)
 }

Comment on lines +240 to +254
func (info *FeeDiscountStakingInfo) GetSortedNewFeeDiscountAccountTiers() []*AccountTierTTL {
accountTiers := make([]*AccountTierTTL, 0, len(info.AccountFeeTiers))
info.NewAccountsMux.RLock()
for k, v := range info.NewAccounts {
accountTiers = append(accountTiers, &AccountTierTTL{
Account: sdk.AccAddress([]byte(string(k[:]))),
TierTTL: v,
})
}
info.NewAccountsMux.RUnlock()
sort.SliceStable(accountTiers, func(i, j int) bool {
return bytes.Compare(accountTiers[i].Account.Bytes(), accountTiers[j].Account.Bytes()) < 0
})
return accountTiers
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Incorrect slice capacity pre-allocation.

Line 241 uses len(info.AccountFeeTiers) as capacity, but the loop iterates over info.NewAccounts. This may cause unnecessary reallocations if the maps have different sizes.

🔎 Proposed fix
 func (info *FeeDiscountStakingInfo) GetSortedNewFeeDiscountAccountTiers() []*AccountTierTTL {
-	accountTiers := make([]*AccountTierTTL, 0, len(info.AccountFeeTiers))
+	accountTiers := make([]*AccountTierTTL, 0, len(info.NewAccounts))
 	info.NewAccountsMux.RLock()
 	for k, v := range info.NewAccounts {
🤖 Prompt for AI Agents
In chain/exchange/types/v2/fee_discounts.go around lines 240 to 254, the slice
capacity is preallocated with len(info.AccountFeeTiers) but the loop iterates
over info.NewAccounts; change the capacity to len(info.NewAccounts) (or 0 if you
prefer letting append grow) so the slice is pre-sized correctly, keep the
lock/unlock and rest of logic unchanged.

Comment on lines +256 to +272
func (info *FeeDiscountStakingInfo) GetSortedAccountVolumeContributions() []*AccountContribution {
accountContributions := make([]*AccountContribution, 0, len(info.AccountFeeTiers))
info.AccountVolumesMux.RLock()
for k, v := range info.AccountVolumeContributions {
accountContributions = append(accountContributions, &AccountContribution{
// use copy of value in closure, since the memory is not copied, it's reused.
// So if your closure captures it, instead of copying via call args, you'll get same index in all goroutines
Account: sdk.AccAddress([]byte(string(k[:]))),
Amount: v,
})
}
info.AccountVolumesMux.RUnlock()
sort.SliceStable(accountContributions, func(i, j int) bool {
return bytes.Compare(accountContributions[i].Account.Bytes(), accountContributions[j].Account.Bytes()) < 0
})
return accountContributions
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Incorrect slice capacity pre-allocation.

Line 257 uses len(info.AccountFeeTiers) as capacity, but the loop iterates over info.AccountVolumeContributions.

🔎 Proposed fix
 func (info *FeeDiscountStakingInfo) GetSortedAccountVolumeContributions() []*AccountContribution {
-	accountContributions := make([]*AccountContribution, 0, len(info.AccountFeeTiers))
+	accountContributions := make([]*AccountContribution, 0, len(info.AccountVolumeContributions))
 	info.AccountVolumesMux.RLock()
 	for k, v := range info.AccountVolumeContributions {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (info *FeeDiscountStakingInfo) GetSortedAccountVolumeContributions() []*AccountContribution {
accountContributions := make([]*AccountContribution, 0, len(info.AccountFeeTiers))
info.AccountVolumesMux.RLock()
for k, v := range info.AccountVolumeContributions {
accountContributions = append(accountContributions, &AccountContribution{
// use copy of value in closure, since the memory is not copied, it's reused.
// So if your closure captures it, instead of copying via call args, you'll get same index in all goroutines
Account: sdk.AccAddress([]byte(string(k[:]))),
Amount: v,
})
}
info.AccountVolumesMux.RUnlock()
sort.SliceStable(accountContributions, func(i, j int) bool {
return bytes.Compare(accountContributions[i].Account.Bytes(), accountContributions[j].Account.Bytes()) < 0
})
return accountContributions
}
func (info *FeeDiscountStakingInfo) GetSortedAccountVolumeContributions() []*AccountContribution {
accountContributions := make([]*AccountContribution, 0, len(info.AccountVolumeContributions))
info.AccountVolumesMux.RLock()
for k, v := range info.AccountVolumeContributions {
accountContributions = append(accountContributions, &AccountContribution{
// use copy of value in closure, since the memory is not copied, it's reused.
// So if your closure captures it, instead of copying via call args, you'll get same index in all goroutines
Account: sdk.AccAddress([]byte(string(k[:]))),
Amount: v,
})
}
info.AccountVolumesMux.RUnlock()
sort.SliceStable(accountContributions, func(i, j int) bool {
return bytes.Compare(accountContributions[i].Account.Bytes(), accountContributions[j].Account.Bytes()) < 0
})
return accountContributions
}
🤖 Prompt for AI Agents
In chain/exchange/types/v2/fee_discounts.go around lines 256-272, the slice is
preallocated with len(info.AccountFeeTiers) but the loop iterates over
info.AccountVolumeContributions; change the capacity to
len(info.AccountVolumeContributions) when calling make so the slice is correctly
sized for the data being appended.

Comment on lines 259 to 263
rpc SubaccountPositions(QuerySubaccountPositionsRequest)
returns (QuerySubaccountPositionsResponse) {
option (google.api.http).get =
"/injective/exchange/v1beta1/positions/{subaccount_id}";
"/injective/exchange/v1beta1/subaccount_positions/{subaccount_id}";
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for references to the old HTTP path pattern

echo "=== Searching for old path pattern (positions/{subaccount_id}) ==="
rg "positions/\{subaccount_id\}" -C2

echo -e "\n=== Searching for alternative path patterns ==="
rg "positions.*subaccount_id" -C2

echo -e "\n=== Searching for SubaccountPositions references ==="
rg "SubaccountPositions" -C3

echo -e "\n=== Checking for HTTP path definitions in query.proto ==="
rg "google.api.http" proto/injective/exchange/v1beta1/query.proto -C2

Repository: InjectiveLabs/sdk-go

Length of output: 50376


API endpoint path clarified, but breaking change for REST API clients.

The updated path /injective/exchange/v1beta1/subaccount_positions/{subaccount_id} is more explicit and aligns better with the SubaccountPositions method name, distinguishing it from the general Positions endpoint.

This is a breaking change for external REST API clients directly calling this endpoint. However, SDK users relying on the generated gRPC client method (FetchChainSubaccountPositions) are unaffected since they use the RPC layer, not hardcoded HTTP paths.

Ensure that REST API clients and downstream services relying on direct HTTP calls are updated with the new path.

🤖 Prompt for AI Agents
In proto/injective/exchange/v1beta1/query.proto around lines 259-263, the HTTP
path for SubaccountPositions was changed to
/injective/exchange/v1beta1/subaccount_positions/{subaccount_id}, which breaks
existing REST clients; to fix this without breaking clients, add an additional
HTTP binding that preserves the old path (using
google.api.http.additional_bindings) mapping the previous endpoint to the same
RPC, or if you intend to break compatibility, update the API docs/changelog and
notify downstreams and SDK maintainers to replace the old REST URL with the new
one.

Comment on lines +79 to +85
// Retrieves the state for all Chainlink Data Streamsprice feeds
rpc ChainlinkDataStreamsPriceStates(
QueryChainlinkDataStreamsPriceStatesRequest)
returns (QueryChainlinkDataStreamsPriceStatesResponse) {
option (google.api.http).get =
"/injective/oracle/v1beta1/chainlink_data_stream_price_states";
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor typo in comment and HTTP path inconsistency.

  1. Line 79: "Streamsprice" should be "Streams price" (missing space)
  2. HTTP path uses singular chainlink_data_stream_price_states while the RPC/message names use plural ChainlinkDataStreams - consider aligning for consistency (e.g., chainlink_data_streams_price_states)
🔎 Suggested fix
-  // Retrieves the state for all Chainlink Data Streamsprice feeds
+  // Retrieves the state for all Chainlink Data Streams price feeds
   rpc ChainlinkDataStreamsPriceStates(
       QueryChainlinkDataStreamsPriceStatesRequest)
       returns (QueryChainlinkDataStreamsPriceStatesResponse) {
     option (google.api.http).get =
-        "/injective/oracle/v1beta1/chainlink_data_stream_price_states";
+        "/injective/oracle/v1beta1/chainlink_data_streams_price_states";
   }
🤖 Prompt for AI Agents
In proto/injective/oracle/v1beta1/query.proto around lines 79 to 85, fix the
comment typo by changing "Streamsprice" to "Streams price" and make the HTTP
path consistent with the RPC/message pluralization by renaming the option path
from "/injective/oracle/v1beta1/chainlink_data_stream_price_states" to
"/injective/oracle/v1beta1/chainlink_data_streams_price_states"; also search for
and update any other references to the old singular path to avoid mismatches.

@aarmoa aarmoa merged commit 37c2e4c into dev Jan 23, 2026
5 of 6 checks passed
@aarmoa aarmoa deleted the c-655/add_chainlink_data_streams_oracle branch January 23, 2026 14:21
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@chain/exchange/types/wasm_trade_summary.go`:
- Around line 68-100: The validateTrades method in SyntheticTradeAction
currently only compares fields pairwise and skips running individual trade
validation; call each SyntheticTrade's Validate() for both userTrade and
contractTrade inside the loop (or before parity checks) and return a wrapped
ErrInvalidTrade that includes the index and whether the failing trade is a user
or contract trade when Validate() errors; ensure you reference the
SyntheticTrade.Validate method and the UserTrades/ContractTrades slices and keep
existing parity checks after successful per-trade validation.

In `@chain/oracle/types/msgs.go`:
- Around line 322-329: MsgRelayChainlinkPrices.ValidateBasic currently only
checks Sender; add light sanity checks on the reports to reject no-op/malformed
submissions: ensure msg.Reports is not nil/empty, iterate reports and reject if
any entry is nil or has missing/zero-required fields (e.g. empty feed/asset
identifier, roundId == 0, timestamp == 0, and any numeric price/answer/aggregate
fields that must be non-zero for a valid report), and return a descriptive
error; keep the existing sdk.AccAddressFromBech32(msg.Sender) check and return
early on failure.
- Around line 31-38: The MsgRelayChainlinkPrices message is not registered in
the codec/interface registry; update RegisterLegacyAminoCodec and
RegisterInterfaces to include MsgRelayChainlinkPrices alongside the other
MsgRelay* types so it can be (de)serialized. In RegisterLegacyAminoCodec add the
MsgRelayChainlinkPrices concrete type to the amino registration block (same
style as MsgRelayPythPrices/MsgRelayStorkPrices), and in RegisterInterfaces add
the MsgRelayChainlinkPrices type to the sdk.Msg interface registrations and to
the msg service descriptor registration so MsgRelayChainlinkPrices is
discoverable by the interface registry.

In `@chain/oracle/types/params.go`:
- Around line 55-58: The field AcceptUnverifiedChainlinkDataStreamsReports is
defaulting to true which permits unverified Chainlink data; confirm intended
behavior and if production/mainnet should require verification, change the
default in the params struct initializer (symbol:
AcceptUnverifiedChainlinkDataStreamsReports in params.go) to false, update any
configuration loading/flag parsing that sets or overrides this value
(constructor/validation functions that reference this field), and add/update
unit tests and docs to reflect the hardened default so behavior is explicit.

In `@Makefile`:
- Around line 6-7: The Makefile target clone-injective-core currently pins the
git clone to pre-release branch v1.18.0-alpha.3; either document this decision
or pin to a stable release: update the clone-injective-core target (the git
clone ... -b ... flag) to use v1.17.2 if you want stable compatibility, or add a
clear comment above the target explaining why v1.18.0-alpha.3 is required (e.g.,
network/feature dependency) and note that it is a pre-release so
reviewers/consumers are aware; make the change directly in the Makefile around
the clone-injective-core target and the git clone -b argument.
♻️ Duplicate comments (3)
chain/exchange/types/v2/codec.go (1)

205-224: Register DenomMinNotionalProposal as govtypes.Content.

It’s registered in Amino but still missing from the gov content interface registry, so decoding/dispatch can fail for governance proposals of this type.

🐛 Proposed fix
 registry.RegisterImplementations(
 	(*govtypes.Content)(nil),
 	&ExchangeEnableProposal{},
 	&BatchExchangeModificationProposal{},
 	&SpotMarketParamUpdateProposal{},
 	&SpotMarketLaunchProposal{},
 	&PerpetualMarketLaunchProposal{},
 	&ExpiryFuturesMarketLaunchProposal{},
 	&DerivativeMarketParamUpdateProposal{},
 	&MarketForcedSettlementProposal{},
 	&UpdateAuctionExchangeTransferDenomDecimalsProposal{},
 	&TradingRewardCampaignLaunchProposal{},
 	&TradingRewardCampaignUpdateProposal{},
 	&TradingRewardPendingPointsUpdateProposal{},
 	&FeeDiscountProposal{},
 	&BatchCommunityPoolSpendProposal{},
 	&BinaryOptionsMarketParamUpdateProposal{},
 	&BinaryOptionsMarketLaunchProposal{},
 	&AtomicMarketOrderFeeMultiplierScheduleProposal{},
+	&DenomMinNotionalProposal{},
 )
chain/exchange/types/v2/derivative.go (2)

44-85: Guard against zero Quantity before division.

order.Margin.Mul(order.Fillable).Quo(order.Quantity) will panic when Quantity is zero.

🔒 Proposed fix
-		remainingMargin := order.Margin.Mul(order.Fillable).Quo(order.Quantity)
+		if order.Quantity.IsZero() {
+			continue
+		}
+		remainingMargin := order.Margin.Mul(order.Fillable).Quo(order.Quantity)

859-863: Guard against nil/zero mark price in synthetic VWAP delta.

vwapInfo or MarkPrice can be nil (or zero), which would panic on division.

🔒 Proposed fix
 func (p *DerivativeVwapInfo) ComputeSyntheticVwapUnitDelta(marketID common.Hash) math.LegacyDec {
 	vwapInfo := p.PerpetualVwapInfo[marketID]
-	return vwapInfo.VwapData.Price.Sub(*vwapInfo.MarkPrice).Quo(*vwapInfo.MarkPrice)
+	if vwapInfo == nil || vwapInfo.MarkPrice == nil || vwapInfo.MarkPrice.IsZero() {
+		return math.LegacyZeroDec()
+	}
+	return vwapInfo.VwapData.Price.Sub(*vwapInfo.MarkPrice).Quo(*vwapInfo.MarkPrice)
 }
🧹 Nitpick comments (6)
chain/exchange/types/wasm_trades.go (1)

77-79: Consider adding a nil guard for defensive safety.

If IsReduceOnly() is called on an unvalidated SyntheticTrade where Margin.IsNil() is true, calling IsZero() could panic. While the normal flow validates first via Validate(), adding a nil check would make this method safer for standalone use.

♻️ Suggested defensive implementation
 func (t *SyntheticTrade) IsReduceOnly() bool {
-	return t.Margin.IsZero()
+	return !t.Margin.IsNil() && t.Margin.IsZero()
 }
chain/exchange/types/wasm_trade_summary.go (1)

32-44: Consider adding a nil guard in Update() for defensive programming.

Currently, Markets is always initialized before Update() is called via initSummary() (line 62), so no panic risk exists in the current codebase. However, since SyntheticTradeActionSummary is an exported type, adding a defensive nil check is good practice to protect against external misuse:

if s.Markets == nil {
    s.Markets = make(map[common.Hash]bool)
}

Regarding the address-only validation (line 39): confirm whether comparing address directly is intentional, or if the full SubaccountID should be validated for consistency.

chain/exchange/types/v2/params.go (1)

360-367: IsHexAddress does not validate EIP-55 checksums—it accepts any valid 40-character hex string including incorrect checksums.

ethcommon.IsHexAddress only checks for a valid 0x prefix (optional), exactly 40 hex characters, and valid hex range; it returns true for all-lowercase, all-uppercase, and mixed-case addresses regardless of checksum correctness. If your policy requires EIP-55 checksum enforcement, use common.MixedcaseAddress(...).ValidChecksum() instead.

chain/exchange/types/proposal.go (1)

1655-1665: Consider including the admin address in invalid-address errors (optional).

Wrapping with err.Error() drops the failing address; using a.Admin improves diagnostics.

♻️ Suggested tweak
-		if _, err := sdk.AccAddressFromBech32(a.Admin); err != nil {
-			return errors.Wrap(ErrInvalidAddress, err.Error())
-		}
+		if _, err := sdk.AccAddressFromBech32(a.Admin); err != nil {
+			return errors.Wrap(ErrInvalidAddress, a.Admin)
+		}
chain/exchange/types/v2/proposal.go (2)

277-278: Expose the minimal‑protocol‑fee flag in the derivative update constructor.

ValidateBasic treats HasDisabledMinimalProtocolFee as an update, but NewDerivativeMarketParamUpdateProposal still can’t set it directly. Consider adding a constructor parameter for symmetry and API completeness.

Also applies to: 545-546, 1746-1747


1893-1903: Consider including the admin address in invalid-address errors (optional).

Wrapping with err.Error() drops the failing address; using a.Admin improves diagnostics.

♻️ Suggested tweak
-		if _, err := sdk.AccAddressFromBech32(a.Admin); err != nil {
-			return errors.Wrap(types.ErrInvalidAddress, err.Error())
-		}
+		if _, err := sdk.AccAddressFromBech32(a.Admin); err != nil {
+			return errors.Wrap(types.ErrInvalidAddress, a.Admin)
+		}

Comment on lines +68 to +100
func (a *SyntheticTradeAction) validateTrades() error {
if len(a.UserTrades) == 0 || len(a.ContractTrades) == 0 {
return errors.Wrapf(ErrInvalidTrade, "no trades in action")
}

if len(a.UserTrades) != len(a.ContractTrades) {
return errors.Wrapf(
ErrInvalidTrade,
"mismatched user and contract trades: %d vs %d",
len(a.UserTrades),
len(a.ContractTrades),
)
}

for i, userTrade := range a.UserTrades {
contractTrade := a.ContractTrades[i]

if userTrade.MarketID != contractTrade.MarketID {
return errors.Wrapf(ErrInvalidTrade, "mismatched user and contract trade at index %d", i)
}
if userTrade.IsBuy == contractTrade.IsBuy {
return errors.Wrapf(ErrInvalidTrade, "mismatched user and contract trade at index %d", i)
}
if !userTrade.Quantity.Equal(contractTrade.Quantity) {
return errors.Wrapf(ErrInvalidTrade, "mismatched user and contract trade at index %d", i)
}
if !userTrade.Price.Equal(contractTrade.Price) {
return errors.Wrapf(ErrInvalidTrade, "mismatched user and contract trade at index %d", i)
}
}
return true
}

func (a *SyntheticTradeAction) Summarize() (*SyntheticTradeActionSummary, error) {
summary := NewSyntheticTradeActionSummary()
return nil
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find the file and explore the surrounding context
find . -name "wasm_trade_summary.go" -type f

Repository: InjectiveLabs/sdk-go

Length of output: 184


🏁 Script executed:

# Look for trade type definitions and IsValid methods
rg -n "type.*Trade.*struct" --type=go -A 5

Repository: InjectiveLabs/sdk-go

Length of output: 50376


🏁 Script executed:

# Check for IsValid methods on trade-related types
rg -n "func.*IsValid" --type=go -B 2 -A 3

Repository: InjectiveLabs/sdk-go

Length of output: 3129


🏁 Script executed:

# Search for SyntheticTrade usage patterns
rg -n "SyntheticTrade" --type=go -B 2 -A 2 | head -100

Repository: InjectiveLabs/sdk-go

Length of output: 5718


🏁 Script executed:

# Look at the specific file to understand the complete context
cat chain/exchange/types/wasm_trade_summary.go 2>/dev/null | head -150

Repository: InjectiveLabs/sdk-go

Length of output: 3073


🏁 Script executed:

# Check if there's a previous version or git history that shows Validate() being called
cd chain/exchange/types && git log --oneline -n 20 wasm_trade_summary.go 2>/dev/null || echo "Git history not available"

Repository: InjectiveLabs/sdk-go

Length of output: 135


🏁 Script executed:

# Verify that nil slice elements are possible by checking how trades are created/unmarshaled
rg -n "UserTrades\s*=" chain/exchange/types/ --type=go -B 2 -A 2

Repository: InjectiveLabs/sdk-go

Length of output: 46


🏁 Script executed:

# Check how SyntheticTradeAction is instantiated/deserialized
rg -n "SyntheticTradeAction" chain/exchange/types/wasm_trades.go --type=go -A 5

Repository: InjectiveLabs/sdk-go

Length of output: 425


Add per-trade validation in validateTrades.
The function only validates pairwise parity (market/buy/qty/price) and skips individual trade validation. The Validate() method on SyntheticTrade checks non-zero market IDs, valid subaccount IDs, and positive quantity/price—guards that should not be bypassed.

🛠️ Suggested fix
 	for i, userTrade := range a.UserTrades {
 		contractTrade := a.ContractTrades[i]
+		if err := userTrade.Validate(); err != nil {
+			return errors.Wrapf(ErrInvalidTrade, "invalid user trade at index %d: %v", i, err)
+		}
+		if err := contractTrade.Validate(); err != nil {
+			return errors.Wrapf(ErrInvalidTrade, "invalid contract trade at index %d: %v", i, err)
+		}
 
 		if userTrade.MarketID != contractTrade.MarketID {
 			return errors.Wrapf(ErrInvalidTrade, "mismatched user and contract trade at index %d", i)
 		}
🤖 Prompt for AI Agents
In `@chain/exchange/types/wasm_trade_summary.go` around lines 68 - 100, The
validateTrades method in SyntheticTradeAction currently only compares fields
pairwise and skips running individual trade validation; call each
SyntheticTrade's Validate() for both userTrade and contractTrade inside the loop
(or before parity checks) and return a wrapped ErrInvalidTrade that includes the
index and whether the failing trade is a user or contract trade when Validate()
errors; ensure you reference the SyntheticTrade.Validate method and the
UserTrades/ContractTrades slices and keep existing parity checks after
successful per-trade validation.

Comment on lines 31 to 38
var (
_ sdk.Msg = &MsgRelayPriceFeedPrice{}
_ sdk.Msg = &MsgRelayBandRates{}
_ sdk.Msg = &MsgRelayCoinbaseMessages{}
_ sdk.Msg = &MsgRequestBandIBCRates{}
_ sdk.Msg = &MsgRelayProviderPrices{}
_ sdk.Msg = &MsgRelayPythPrices{}
_ sdk.Msg = &MsgRelayStorkPrices{}
_ sdk.Msg = &MsgRelayChainlinkPrices{}
_ sdk.Msg = &MsgUpdateParams{}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for MsgRelayChainlinkPrices references
rg -n --type=go 'MsgRelayChainlinkPrices' -A 2

Repository: InjectiveLabs/sdk-go

Length of output: 11855


🏁 Script executed:

#!/bin/bash
# Find codec and registry related files
find . -type f -name "*.go" | xargs grep -l "RegisterConcrete\|RegisterInterface" | head -20

Repository: InjectiveLabs/sdk-go

Length of output: 668


🏁 Script executed:

#!/bin/bash
# Look for codec.go files and registry patterns
fd -e go -p ".*codec.*" | head -20

Repository: InjectiveLabs/sdk-go

Length of output: 604


🏁 Script executed:

cat -n chain/oracle/types/codec.go

Repository: InjectiveLabs/sdk-go

Length of output: 3880


🏁 Script executed:

#!/bin/bash
# Search for codec registration patterns in oracle module
rg -n "RegisterConcrete|RegisterInterface" chain/oracle/types/codec.go -A 1 -B 1

Repository: InjectiveLabs/sdk-go

Length of output: 1504


Register MsgRelayChainlinkPrices in codec/interface registry.
MsgRelayChainlinkPrices is missing from both RegisterLegacyAminoCodec() (line 18-22) and RegisterInterfaces() (line 35-41) in chain/oracle/types/codec.go. Add the message type to both registration functions alongside the other message types to ensure proper transaction serialization and deserialization.

🤖 Prompt for AI Agents
In `@chain/oracle/types/msgs.go` around lines 31 - 38, The MsgRelayChainlinkPrices
message is not registered in the codec/interface registry; update
RegisterLegacyAminoCodec and RegisterInterfaces to include
MsgRelayChainlinkPrices alongside the other MsgRelay* types so it can be
(de)serialized. In RegisterLegacyAminoCodec add the MsgRelayChainlinkPrices
concrete type to the amino registration block (same style as
MsgRelayPythPrices/MsgRelayStorkPrices), and in RegisterInterfaces add the
MsgRelayChainlinkPrices type to the sdk.Msg interface registrations and to the
msg service descriptor registration so MsgRelayChainlinkPrices is discoverable
by the interface registry.

Comment on lines +322 to +329
// ValidateBasic implements the sdk.Msg interface for MsgRelayChainlinkPrices.
func (msg MsgRelayChainlinkPrices) ValidateBasic() error {
if _, err := sdk.AccAddressFromBech32(msg.Sender); err != nil {
return err
}

return nil
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add basic report sanity checks in ValidateBasic.
Currently empty or nil reports are accepted; this can allow no-op or malformed submissions.

✅ Suggested minimal validation
 func (msg MsgRelayChainlinkPrices) ValidateBasic() error {
 	if _, err := sdk.AccAddressFromBech32(msg.Sender); err != nil {
 		return err
 	}
+
+	if len(msg.Reports) == 0 {
+		return errors.Wrap(sdkerrors.ErrInvalidRequest, "reports must not be empty")
+	}
+	for i, r := range msg.Reports {
+		if r == nil {
+			return errors.Wrapf(sdkerrors.ErrInvalidRequest, "reports[%d] is nil", i)
+		}
+	}

 	return nil
 }
🤖 Prompt for AI Agents
In `@chain/oracle/types/msgs.go` around lines 322 - 329,
MsgRelayChainlinkPrices.ValidateBasic currently only checks Sender; add light
sanity checks on the reports to reject no-op/malformed submissions: ensure
msg.Reports is not nil/empty, iterate reports and reject if any entry is nil or
has missing/zero-required fields (e.g. empty feed/asset identifier, roundId ==
0, timestamp == 0, and any numeric price/answer/aggregate fields that must be
non-zero for a valid report), and return a descriptive error; keep the existing
sdk.AccAddressFromBech32(msg.Sender) check and return early on failure.

Comment on lines +55 to +58
PythContract: "",
ChainlinkVerifierProxyContract: "",
AcceptUnverifiedChainlinkDataStreamsReports: true,
ChainlinkDataStreamsVerificationGasLimit: 500_000,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Confirm default “accept unverified” is intended.
AcceptUnverifiedChainlinkDataStreamsReports defaults to true, which could allow unverified price data. Please confirm this is the intended default for mainnet/production; otherwise consider defaulting to false.

#!/bin/bash
# Inspect how the unverified flag is used to gate verification.
rg -n --type=go 'AcceptUnverifiedChainlinkDataStreamsReports'
🔧 Potential default hardening (if verification should be required)
-        AcceptUnverifiedChainlinkDataStreamsReports: true,
+        AcceptUnverifiedChainlinkDataStreamsReports: false,
🤖 Prompt for AI Agents
In `@chain/oracle/types/params.go` around lines 55 - 58, The field
AcceptUnverifiedChainlinkDataStreamsReports is defaulting to true which permits
unverified Chainlink data; confirm intended behavior and if production/mainnet
should require verification, change the default in the params struct initializer
(symbol: AcceptUnverifiedChainlinkDataStreamsReports in params.go) to false,
update any configuration loading/flag parsing that sets or overrides this value
(constructor/validation functions that reference this field), and add/update
unit tests and docs to reflect the hardened default so behavior is explicit.

Comment thread Makefile
Comment on lines 6 to +7
clone-injective-core:
git clone https://github.com/InjectiveLabs/injective-core.git -b v1.17.0 --depth 1 --single-branch
git clone https://github.com/InjectiveLabs/injective-core.git -b v1.18.0-alpha.3 --depth 1 --single-branch
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

injective-core latest stable release version

💡 Result:

The latest stable release of injective-core (Docker image injectivelabs/injective-core) is v1.17.2. (github.com)

Citations:


🏁 Script executed:

# Check if there's any documentation in the repo about versioning strategy
find . -type f \( -name "*.md" -o -name "*.txt" \) | head -20 | xargs grep -l "injective-core\|version" 2>/dev/null | head -5

Repository: InjectiveLabs/sdk-go

Length of output: 184


🏁 Script executed:

# Check git history of the Makefile to see version progression
git log --oneline -p Makefile 2>/dev/null | grep -A 2 -B 2 "injective-core" | head -30

Repository: InjectiveLabs/sdk-go

Length of output: 2080


Confirm alpha version pinning—either wait for stable v1.18.0 or document pre-release status.

The clone target uses v1.18.0-alpha.3, a pre-release version. The latest stable release is v1.17.2. If this alpha pin is intentional to support an upcoming chain release, add a comment in the Makefile explaining the reasoning. Otherwise, consider waiting for a stable v1.18.0 release or pin to v1.17.2 if compatibility is not required.

🤖 Prompt for AI Agents
In `@Makefile` around lines 6 - 7, The Makefile target clone-injective-core
currently pins the git clone to pre-release branch v1.18.0-alpha.3; either
document this decision or pin to a stable release: update the
clone-injective-core target (the git clone ... -b ... flag) to use v1.17.2 if
you want stable compatibility, or add a clear comment above the target
explaining why v1.18.0-alpha.3 is required (e.g., network/feature dependency)
and note that it is a pre-release so reviewers/consumers are aware; make the
change directly in the Makefile around the clone-injective-core target and the
git clone -b argument.

@coderabbitai coderabbitai bot mentioned this pull request Feb 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants